package torres.ability;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import torres.action.Action;
import torres.action.AngularAction;
import torres.action.Effect;
import torres.action.LinearAction;
import torres.action.RadialAction;
import torres.entity.Occupation.Job;
import torres.entity.Statistic;

/**
 * @author Torres
 * Reads in and instantiates all Abilities a ability file and then
 * acts as a repository for those abilities.
 */
public class AbilityManager {
	
	private static AbilityManager instance;
	
	private Map<Integer, ActionAbility> actionAbilities;
	private Map<Integer, NonActionAbility> nonActionAbilities; 
	private Map<Integer, List<Integer>> smasherLevelRequirements;
	private Map<Integer, List<Integer>> summonerLevelRequirements;
	private Map<Integer, List<Integer>> sneakLevelRequirements;
	
	/**
	 * Creates a new AbilityManager.
	 */
	private AbilityManager() {
		
		actionAbilities = new HashMap<Integer, ActionAbility>();
		nonActionAbilities = new HashMap<Integer, NonActionAbility>();
		smasherLevelRequirements = new HashMap<Integer, List<Integer>>();
		summonerLevelRequirements = new HashMap<Integer, List<Integer>>();
		sneakLevelRequirements = new HashMap<Integer, List<Integer>>();
	}
	
	/**
	 * Retrieves the single instance of AbilityManager.
	 * @return The single instance of AbilityManager.
	 */
	public static AbilityManager getInstance() {
		
		if(instance == null)
			instance = new AbilityManager();
		return instance;
	}
	
	/**
	 * Initialize the abilities from the specified file to be retrieved later.
	 * @param file The file to be parsed.
	 */
	public void generate(File file) {
		
		try {
			Scanner reader = new Scanner(file);
			
			//read in Attack ability
			reader.nextLine(); //throw away the "--Attack--" String
			String line;
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split("\\s+");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Action> actions = new ArrayList<Action>();
				
				int paramNum = 6;
				for(int i = 2; i < data.length; i+=paramNum) {
					String s = data[i];
					Effect elem = Enum.valueOf(Effect.class, s);
					Action a = null;
					
					char c = data[i+1].charAt(0);
					int range = Integer.parseInt(data[i+2]);
					int power = Integer.parseInt(data[i+3]);
					int accuracy = Integer.parseInt(data[i+4]);
					int speed = Integer.parseInt(data[i+5]);
					
					switch(c) {
						case 'R': 
							a = new RadialAction(elem, range, power, accuracy, speed);
							break;
						case 'A': 
							a = new AngularAction(elem, range, power, accuracy, speed);
							break;
						case 'L':
							a = new LinearAction(elem, range, power, accuracy, speed);
							break;
						default: 
							System.err.println("AbilityManager: INVALID ACTION TYPE");
							break;
					}
					actions.add(a);
				}
				data = reader.nextLine().split("\\s+");
				//System.out.println("AbilityManager: " + Arrays.toString(data));
				List<Cost> costs = new ArrayList<Cost>();
				//System.out.println("AbilityManager: " + data[1]);
				Cost c = new Cost(Statistic.valueOf(data[1]), Integer.parseInt(data[2]));
				costs.add(c);
				ActionAbility a = new ActionAbility(name, id, actions, costs);
				actionAbilities.put(id, a);
			}
			
			//read in Enchantment abilities
			reader.nextLine(); //throw away the "--Attack--" String
			//String line;
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split("\\s+");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Action> actions = new ArrayList<Action>();
				
				int paramNum = 6;
				for(int i = 2; i < data.length; i+=paramNum) {
					String s = data[i];
					Effect elem = Enum.valueOf(Effect.class, s);
					Action a = null;
					
					char c = data[i+1].charAt(0);
					int range = Integer.parseInt(data[i+2]);
					int power = Integer.parseInt(data[i+3]);
					int accuracy = Integer.parseInt(data[i+4]);
					int speed = Integer.parseInt(data[i+5]);
					
					switch(c) {
						case 'R': 
							a = new RadialAction(elem, range, power, accuracy, speed);
							break;
						case 'A': 
							a = new AngularAction(elem, range, power, accuracy, speed);
							break;
						case 'L':
							a = new LinearAction(elem, range, power, accuracy, speed);
							break;
						default: 
							System.err.println("AbilityManager: INVALID ACTION TYPE");
							break;
					}
					actions.add(a);
				}
				data = reader.nextLine().split("\\s+");
				List<Cost> costs = new ArrayList<Cost>();
				Cost c = new Cost(Statistic.valueOf(data[1]), Integer.parseInt(data[2]));
				costs.add(c);
				ActionAbility a = new ActionAbility(name, id, actions, costs);
				actionAbilities.put(id, a);
			}
			
			//read in Boon abilities
			reader.nextLine(); //throw away the "--Boon--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split("\\s+");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Action> actions = new ArrayList<Action>();
				
				int paramNum = 6;
				for(int i = 2; i < data.length; i+=paramNum) {
					String s = data[i];
					Effect elem = Enum.valueOf(Effect.class, s);
					Action a = null;
					
					char c = data[i+1].charAt(0);
					int range = Integer.parseInt(data[i+2]);
					int power = Integer.parseInt(data[i+3]);
					int accuracy = Integer.parseInt(data[i+4]);
					int speed = Integer.parseInt(data[i+5]);
					switch(c) {
						case 'R': 
							a = new RadialAction(elem, range, power, accuracy, speed);
							break;
						case 'A': 
							a = new AngularAction(elem, range, power, accuracy, speed);
							break;
						case 'L':
							a = new LinearAction(elem, range, power, accuracy, speed);
							break;
						default: 
							System.err.println("ITEMMANAGER: INVALID ACTION TYPE");
							break;
					}
					actions.add(a);
				}
				data = reader.nextLine().split("\\s+");
				List<Cost> costs = new ArrayList<Cost>();
				Cost c = new Cost(Statistic.valueOf(data[1]), Integer.parseInt(data[2]));
				costs.add(c);
				ActionAbility a = new ActionAbility(name, id, actions, costs);
				actionAbilities.put(id, a);
			}
			
			//read in Bane items
			reader.nextLine(); //throw away the "--Bane--" String
			while(!(line = reader.nextLine()).equals("")) {
				String[] data = line.split("\\s+");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				List<Action> actions = new ArrayList<Action>();
				int paramNum = 6;
				for(int i = 2; i < data.length; i+=paramNum) {
					String s = data[i];
					Effect elem = Enum.valueOf(Effect.class, s);
					Action a = null;
					
					char c = data[i+1].charAt(0);
					int range = Integer.parseInt(data[i+2]);
					int power = Integer.parseInt(data[i+3]);
					int accuracy = Integer.parseInt(data[i+4]);
					int speed = Integer.parseInt(data[i+5]);
					switch(c) {
						case 'R': 
							a = new RadialAction(elem, range, power, accuracy, speed);
							break;
						case 'A': 
							a = new AngularAction(elem, range, power, accuracy, speed);
							break;
						case 'L':
							a = new LinearAction(elem, range, power, accuracy, speed);
							break;
						default: 
							System.err.println("ITEMMANAGER: INVALID ACTION TYPE");
							break;
					}
					actions.add(a);
				}
				data = reader.nextLine().split("\\s+");
				List<Cost> costs = new ArrayList<Cost>();
				Cost c = new Cost(Statistic.valueOf(data[1]), Integer.parseInt(data[2]));
				costs.add(c);
				ActionAbility a = new ActionAbility(name, id, actions, costs);
				actionAbilities.put(id, a);
			}
			
			//read in Sneak abilities 
			reader.nextLine(); //throw away the "--Sneak--" String
			//while(!(line = reader.nextLine()).equals("")) {
			while(reader.hasNextLine()) {
				line = reader.nextLine();
				String[] data = line.split("\\s+");
				int id = Integer.parseInt(data[0]);
				String name = data[1];
				NonActionAbility n = null;
				switch(name.charAt(0)) {
					case 'c': 
						//data = reader.nextLine().split("\\s+");
						List<Cost> costs = new ArrayList<Cost>();
						Cost c = new Cost(Statistic.valueOf(data[2]), Integer.parseInt(data[3]));
						costs.add(c);
						n = new CreepAbility("Creep", id, costs);
						break;
					case 'p':
						//data = reader.nextLine().split("\\s+");
						costs = new ArrayList<Cost>();
						c = new Cost(Statistic.valueOf(data[2]), Integer.parseInt(data[3]));
						costs.add(c);
						n = new PickPocketAbility("Pickpocket", id, costs);
						break;
					case 'r':
						//data = reader.nextLine().split("\\s+");
						costs = new ArrayList<Cost>();
						c = new Cost(Statistic.valueOf(data[2]), Integer.parseInt(data[3]));
						costs.add(c);
						n = new RemoveTrapAbility("Remove Trap", id, costs);
				}				
				nonActionAbilities.put(id, n);
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Initialize the ability requirements for each Occupation from the specified file to be retrieved later.
	 * @param file The file to be parsed.
	 */
	public void generateLevelRequirementList(File file) {
		
		try {
			Scanner reader = new Scanner(file);
			
			//Read in the Smasher level requirement list
			reader.nextLine();
			String line;
			while (!(line = reader.nextLine()).equals("")) {
				//System.out.println("abilityManager.generateRequirementsList(1)");
				String[] data = line.split("\\s+");
				if (data[0].contains("#") || data[1].contains("#"))
					continue;
				int levelKey = Integer.parseInt(data[0]);
				int hashKey = Integer.parseInt(data[1]);

				ArrayList<Integer> arr;
				if (this.smasherLevelRequirements.get(levelKey) == null)
					arr = new ArrayList<Integer>();
				else
					arr = (ArrayList<Integer>) this.smasherLevelRequirements.get(levelKey);
				arr.add(hashKey);
				this.smasherLevelRequirements.put(levelKey, arr);
			}			
			
			//Read in the Summoner level requirement list
			reader.nextLine();
			while (!(line = reader.nextLine()).equals("")) {
				//System.out.println("abilityManager.generateRequirementsList(1)");
				String[] data = line.split("\\s+");
				if (data[0].contains("#") || data[1].contains("#"))
					continue;
				int levelKey = Integer.parseInt(data[0]);
				int hashKey = Integer.parseInt(data[1]);
				ArrayList<Integer> arr;
				if (this.summonerLevelRequirements.get(levelKey) == null)
					arr = new ArrayList<Integer>();
				else
					arr = (ArrayList<Integer>) this.summonerLevelRequirements.get(levelKey);
				arr.add(hashKey);
				this.summonerLevelRequirements.put(levelKey, arr);
			}
			
			//Read in the Sneak level requirement list
			reader.nextLine();
			while (reader.hasNextLine()) {
				line = reader.nextLine();
				//System.out.println("abilityManager.generateRequirementsList(1)");
				String[] data = line.split("\\s+");
				if (data[0].contains("#") || data[1].contains("#"))
					continue;
				int levelKey = Integer.parseInt(data[0]);
				int hashKey = Integer.parseInt(data[1]);
				ArrayList<Integer> arr;
				if (this.sneakLevelRequirements.get(levelKey) == null)
					arr = new ArrayList<Integer>();
				else
					arr = (ArrayList<Integer>) this.sneakLevelRequirements.get(levelKey);
				arr.add(hashKey);
				this.sneakLevelRequirements.put(levelKey, arr);
			}
			
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Updates occupation ability list with proper occupation-based abilities based on level.  
	 * @param abilityList List of all available abilities
	 * @param level Level of Avatar
	 * @param job Occupation of Avatar
	 */
	public void updateAbilityList(HashSet<Ability> abilityList, int level, Job job) {
		
		while (level > 0) {
			ArrayList<Integer> arr = null;
			switch(job) {
				case SMASHER : arr = (ArrayList<Integer>) this.smasherLevelRequirements.get(level); break;
				case SUMMONER : arr = (ArrayList<Integer>) this.summonerLevelRequirements.get(level); break;
				case SNEAK : arr = (ArrayList<Integer>) this.sneakLevelRequirements.get(level); break;
			}
			level--;
			if (arr == null)
				continue;
			
			for (Integer e : arr) {
				if (!(this.actionAbilities.get(e.intValue()) == null))
					abilityList.add(this.actionAbilities.get(e.intValue()));
				if (!(this.nonActionAbilities.get(e.intValue()) == null))
					abilityList.add(this.nonActionAbilities.get(e.intValue()));
			}
		}
	}
	
	/**
	 * Creates and returns a new instance of an ActionAbility that corresponds to the specified key.
	 * @param key The key of the ability to be retrieved.
	 * @return ActionAbility corresponding to the specified key.
	 */
	public ActionAbility getActionAbility(int key) {
		
		return new ActionAbility(actionAbilities.get(key));
	}
	
	/**
	 * Creates and returns a new instance of an NonActionAbility that corresponds to the specified key.
	 * @param key The key of the ability to be retrieved.
	 * @return NonActionAbility corresponding to the specified key.
	 */
	public NonActionAbility getNonActionAbility(int key) {
		
		return nonActionAbilities.get(key);
	}
}
